/*
 * Copyright (c) Huawei Device Co., Ltd. 2024. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "arf_manager.h"

#include "ef_parser.h"
#include "loghelper.h"
#include "core_service_client.h"
#include "se_common_exception.h"
#include "ef_dir.h"
#include "ef_dodf.h"
#include "ef_odf.h"

namespace OHOS {
namespace SE {
static const uint8_t MIN_CMD_DATA_LEN = 5;
static const int COMMAND_ID_1 = 176;
static const int COMMAND_ID_2 = 178;
static const int COMMAND_ID_3 = 192;
static const uint8_t CMD_P1_OFFSET = 2;
static const uint8_t CMD_P2_OFFSET = 3;
static const uint8_t CMD_P3_OFFSET = 4;

const std::vector<uint8_t> GPAC_ARF_AID = {0xA0, 0x00, 0x00, 0x00, 0x18, 0x47, 0x50, 0x41, 0x43, 0x2D, 0x31, 0x35};
// DEF_PKCS15_AID reference: GlobalPlatform Tcchnology Open Mobile API Spec
const std::vector<uint8_t> DEF_PKCS15_AID = {0xA0, 0x00, 0x00, 0x00, 0x63, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35};
const std::vector<std::vector<uint8_t>> CANDIDATE_AIDS = {DEF_PKCS15_AID, GPAC_ARF_AID, std::vector<uint8_t>()};

ArfManager::ArfManager()
{
}

ArfManager::~ArfManager()
{
}

bool ArfManager::Initialize(std::weak_ptr<SecureElementAgent> seAgent)
{
    InfoLog("ArfManager Initialization start");
    if (seAgent.expired()) {
        ErrorLog("seAgent expired.");
        return false;
    }
    secureElementAgent_ = seAgent;
    acMainPath_.clear();
    GetEfAcMain();
    bool result = LoadArfRules();
    CloseArfChannel();
    return result;
}

void ArfManager::GetEfAcMain()
{
    bool isArfChannelFound = false;
    for (uint8_t index = 0; index < CANDIDATE_AIDS.size(); index++) {
        if (!OpenLogicalArfChannel(CANDIDATE_AIDS[index])) {
            WarnLog("fail to OpenLogicalArfChannel, continue.");
            continue;
        }
        isArfChannelFound = true;
        std::vector<uint8_t> acMainPath;
        InfoLog("current size of ac main path = %{public}lu", acMainPath_.size());
        if (acMainPath_.size() == 0) {
            std::shared_ptr<EfOdf> tempEfOdf = std::make_shared<EfOdf>();
            std::vector<uint8_t> dodfPath = tempEfOdf->ReadOdfFileByPath(odfFilePath_);
            std::shared_ptr<EfDodf> tempEfDodf = std::make_shared<EfDodf>();
            acMainPath = tempEfDodf->ReadDodfFileByPath(dodfPath);
            acMainPath_ = acMainPath;
        } else if (odfFilePath_.size() != 0) {
            acMainPath.clear();
            acMainPath.insert(acMainPath.end(), odfFilePath_.begin(), odfFilePath_.end());
            acMainPath.insert(acMainPath.end(), acMainPath_.begin(), acMainPath_.end());
        } else {
            acMainPath = acMainPath_;
        }
        acMain_ = std::make_shared<EfAcMain>(acMainPath);
        break;
    }

    if (!isArfChannelFound) {
        ClearArfRules();
        ErrorLog("fail to open arf channel by candidate aid.");
    }
}

bool ArfManager::LoadArfRules()
{
    if (acMain_ == nullptr) {
        ErrorLog("acMain nullptr.");
        return false;
    }
    std::vector<uint8_t> acRulesPath = acMain_->ParseAcMainFileByPath();
    if (acRulesPath.size() == 0) {
        InfoLog("fail to get ac rule path, or refresh tag is found.");
        return false;
    }
    if (acRules_ == nullptr) {
        InfoLog("acRules nullptr.");
        acRules_ = std::make_shared<EfAcRules>();
    }
    ClearArfRules();
    acMainPath_.clear();
    CloseArfChannel();

    GetEfAcMain();
    if (!acRules_->ParseAcRulesFileByPath(acRulesPath)) {
        InfoLog("clear access rule cache.");
        ClearArfRules();
        return false;
    }
    return true;
}

bool ArfManager::OpenLogicalArfChannel(const std::vector<uint8_t> &aid)
{
    InfoLog("OpenLogicalArfChannel, aid length [%{public}lu]", aid.size());
    if (aid.size() != 0) {
        arfChannel_ = OpenLogicalArfChannelByAid(aid);
        if (arfChannel_ == nullptr) {
            WarnLog("fail to open logical arf channel by current aid.");
            return false;
        }
        if (odfFilePath_.size() != 0) {
            acMainPath_.clear();
        }
        odfFilePath_.clear();
    } else {
        arfChannel_ = OpenLogicalArfChannelByAid(std::vector<uint8_t>());
        if (arfChannel_ != nullptr) {
            InfoLog("set SE_NAME_ESE.");
            SetUsedSeName(SE_NAME_ESE);
        } else {
            InfoLog("fail to open logical arf channel by current aid, set SIM.");
            if (secureElementAgent_.expired()) {
                ErrorLog("secure element agent expired.");
                return false;
            }
            SetUsedSeName(secureElementAgent_.lock()->GetName());
        }
        if (odfFilePath_.size() == 0) {
            acMainPath_.clear();
            std::shared_ptr<EfDir> tempEfDir = std::make_shared<EfDir>();
            odfFilePath_ = tempEfDir->ParseDirFileData(DEF_PKCS15_AID);
            if (odfFilePath_.size() == 0) {
                WarnLog("unable to use arf.");
                return false;
            }
        }
    }
    return true;
}

sptr<ChannelImpl> ArfManager::OpenLogicalArfChannelByAid(const std::vector<uint8_t> &aid)
{
    if (secureElementAgent_.expired()) {
        ErrorLog("secure element agent expired.");
        return nullptr;
    }
    try {
        std::string rspString;
        arfChannel_ = secureElementAgent_.lock()->OpenLogicalChannelWithoutChannelAccess(
            CommonUtils::BytesArrayToHexString(&aid[0], aid.size()), 0, rspString);
    } catch (const IllegalStateError &e) {
        ErrorLog("IllegalStateError exception happens.");
        return nullptr;
    } catch (const IllegalArgumentError &e) {
        ErrorLog("IllegalArgumentError exception happens");
        return nullptr;
    }

    if (arfChannel_ == nullptr) {
        ErrorLog("arf channel nullptr.");
        return nullptr;
    }
    return arfChannel_;
}

std::vector<uint8_t> ArfManager::TransmitArfApdu(std::shared_ptr<EfParser> efParser, const std::vector<uint8_t> &cmd)
{
    std::string rspString;
    if (usedSeName_ == SE_NAME_SIM || usedSeName_ == SE_NAME_SIM2) {
        TransmitArfApduBySimIO(efParser->GetFileId(), efParser->GetFilePath(), cmd, rspString);
        
        std::string cmdString = CommonUtils::BytesArrayToHexString(&cmd[0], cmd.size());
        InfoLog("ARF TRANSMIT SIM IO cmd --> %{public}s", cmdString.c_str());
        InfoLog("ARF TRANSMIT SIM IO rsp <-- %{public}s", rspString.c_str());
    } else {
        std::string cmdString = CommonUtils::BytesArrayToHexString(&cmd[0], cmd.size());
        arfChannel_->Transmit(cmdString, rspString);
        InfoLog("ARF TRANSMIT cmd --> %{public}s", cmdString.c_str());
        InfoLog("ARF TRANSMIT rsp <-- %{public}s", rspString.c_str());
    }
    std::vector<uint8_t> rspVec;
    CommonUtils::HexStringToBytesArray(rspString, rspVec);
    return rspVec;
}

int32_t ArfManager::TransmitArfApduBySimIO(
    int fileId, const std::vector<uint8_t> &filePath, const std::vector<uint8_t> &cmd, std::string &rsp)
{
    if (cmd.size() < MIN_CMD_DATA_LEN || filePath.size() == 0) {
        ErrorLog("illegal input data length.");
        return -1;
    }
    uint8_t p1 = cmd[CMD_P1_OFFSET];
    uint8_t p2 = cmd[CMD_P2_OFFSET];
    uint8_t p3 = cmd[CMD_P3_OFFSET];
    int commandId = 0;
    switch (cmd[1]) {
        case 0xB0:
            commandId = COMMAND_ID_1;
            break;
        case 0xB2:
            commandId = COMMAND_ID_2;
            break;
        case 0xA4:
            commandId = COMMAND_ID_3;
            p1 = 0;
            p2 = 0;
            p3 = 0x0F;
            break;
        default:
            ErrorLog("unknown sim io cmd[%{public}u]", cmd[1]);
    }
    std::vector<uint8_t> data = {p1, p2, p3};
    std::string dataStr = CommonUtils::BytesArrayToHexString(&data[0], data.size());
    std::string filePathStr = CommonUtils::BytesArrayToHexString(&filePath[0], filePath.size());
    Telephony::SimAuthenticationResponse simRsp;
    int32_t slotId = (usedSeName_ == SE_NAME_SIM) ? 0 : 1;
    int32_t ret = Telephony::CoreServiceClient::GetInstance().GetSimIO(
        slotId, commandId, fileId, dataStr, filePathStr, simRsp);
    rsp = simRsp.response;
    return ret;
}

void ArfManager::CloseArfChannel()
{
    if (arfChannel_ != nullptr) {
        arfChannel_->Close();
        arfChannel_ = nullptr;
    }
}

void ArfManager::AddArfRule(
    shared_ptr<AidRefDo> aidRefDo, std::shared_ptr<DeviceAppIdRefDo> deviceAppIdRefDo, bool isAllowed)
{
    if (aidRefDo == nullptr || deviceAppIdRefDo == nullptr) {
        ErrorLog("invalid input.");
        return;
    }
    std::vector<uint8_t> aidRefDoPayload;
    aidRefDo->GetRawData(aidRefDoPayload);
    std::vector<uint8_t> deviceAppIdRefDoPayload;
    deviceAppIdRefDo->GetRawData(deviceAppIdRefDoPayload);
    aidRefDoPayload.insert(aidRefDoPayload.end(), deviceAppIdRefDoPayload.begin(), deviceAppIdRefDoPayload.end());
    std::shared_ptr<RefDo> tempRefDo = std::make_shared<RefDo>(aidRefDoPayload, true);
    if (tempRefDo == nullptr) {
        ErrorLog("new ref do nullptr");
        return;
    }
    AddArfRule(tempRefDo, isAllowed);
}

void ArfManager::AddArfRule(std::shared_ptr<RefDo> refDo, bool isAllowed)
{
    uint8_t payLoad = isAllowed ? 1 : 0;
    std::vector<uint8_t> apduArDoPayload = {payLoad};
    std::shared_ptr<ApduArDo> tempApduArDo = std::make_shared<ApduArDo>(apduArDoPayload, true);
    if (tempApduArDo == nullptr) {
        ErrorLog("new apdu ar do nullptr.");
        return;
    }
    std::vector<uint8_t> apduArDoRawData;
    tempApduArDo->GetRawData(apduArDoRawData);
    std::shared_ptr<ArDo> tempArDo = std::make_shared<ArDo>(apduArDoRawData, true);
    if (tempArDo == nullptr) {
        ErrorLog("new ar do nullptr.");
        return;
    }
    std::vector<uint8_t> aidRefDoRawData;
    tempArDo->GetRawData(aidRefDoRawData);
    std::vector<uint8_t> refDoRawData;
    refDo->GetRawData(refDoRawData);
    refDoRawData.insert(refDoRawData.end(), aidRefDoRawData.begin(), aidRefDoRawData.end());
    std::shared_ptr<RefArDo> tempRefArDo = std::make_shared<RefArDo>(refDoRawData, true);
    auto vecIt = std::find(arfRuleVec_.begin(), arfRuleVec_.end(), tempRefArDo);
    if (vecIt != arfRuleVec_.end()) {
        InfoLog("RefArDo already added.");
        return;
    }
    arfRuleVec_.push_back(tempRefArDo);
}

std::vector<std::shared_ptr<RefArDo>> ArfManager::GetArfRule()
{
    return arfRuleVec_;
}

void ArfManager::ClearArfRules()
{
    arfRuleVec_.clear();
}

std::string ArfManager::GetUsedSeName()
{
    return usedSeName_;
}

void ArfManager::SetUsedSeName(const std::string &seName)
{
    usedSeName_ = seName;
}

std::vector<uint8_t> ArfManager::GetRefreshTag()
{
    return refreshTag_;
}

void ArfManager::SetRefreshTag(const std::vector<uint8_t> &refreshTag)
{
    refreshTag_ = refreshTag;
}
}  // namespace SE
}  // namespace OHOS
